<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>接口请求工具</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #f5f5f5;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }

        .header {
            background-color: #28a745;
            color: white;
            padding: 20px;
            text-align: center;
        }

        .request-panel {
            padding: 20px;
            border-bottom: 1px solid #eee;
        }

        .url-section {
            display: flex;
            margin-bottom: 20px;
        }

        .method-select {
            width: 100px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px 0 0 4px;
            background-color: #f8f9fa;
            font-weight: bold;
        }

        .url-input {
            flex: 1;
            padding: 10px 15px;
            border: 1px solid #ddd;
            border-left: none;
            border-right: none;
            font-size: 16px;
        }

        .send-button {
            width: 100px;
            padding: 10px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 0 4px 4px 0;
            cursor: pointer;
            font-weight: bold;
            transition: background-color 0.3s;
        }

        .send-button:hover {
            background-color: #218838;
        }

        .params-section {
            margin-bottom: 20px;
        }

        .section-title {
            font-size: 18px;
            margin-bottom: 10px;
            color: #333;
        }

        .param-inputs {
            display: flex;
            gap: 10px;
            margin-bottom: 10px;
        }

        .param-key, .param-value {
            flex: 1;
            padding: 8px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .add-param,.remove-param {
            padding: 8px 15px;
            background-color: #17a2b8;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        .add-param:hover {
            background-color: #138496;
        }

        .remove-param {
            background-color: #dc3545;
        }

        .remove-param:hover {
            background-color: #c82333;
        }

        .file-upload-section {
            margin-bottom: 20px;
        }

        .file-input-container {
            display: flex;
            gap: 10px;
            margin-bottom: 10px;
        }

        .file-key {
            flex: 1;
            padding: 8px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .file-input {
            flex: 2;
            padding: 8px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .file-remove {
            padding: 8px 15px;
            background-color: #dc3545;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        .file-remove:hover {
            background-color: #c82333;
        }

        .add-file {
            padding: 8px 15px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        .add-file:hover {
            background-color: #218838;
        }

        .response-panel {
            padding: 20px;
        }

        .response-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }

        .status-indicator {
            padding: 5px 10px;
            border-radius: 4px;
            font-weight: bold;
        }

        .status-success {
            background-color: #d4edda;
            color: #155724;
        }

        .status-error {
            background-color: #f8d7da;
            color: #721c24;
        }

        .response-content {
            background-color: #f8f9fa;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 15px;
            min-height: 200px;
            max-height: 400px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            white-space: pre-wrap;
        }

        .hidden {
            display: none;
        }

        .loading {
            text-align: center;
            padding: 20px;
        }

        .spinner {
            border: 4px solid rgba(0, 0, 0, 0.1);
            border-radius: 50%;
            border-top: 4px solid #28a745;
            width: 30px;
            height: 30px;
            animation: spin 1s linear infinite;
            margin: 0 auto 10px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .tabs {
            display: flex;
            margin-bottom: 15px;
            border-bottom: 1px solid #ddd;
        }

        .tab {
            padding: 10px 20px;
            cursor: pointer;
            background-color: #f8f9fa;
            border: 1px solid #ddd;
            border-bottom: none;
            border-radius: 4px 4px 0 0;
            margin-right: 5px;
        }

        .tab.active {
            background-color: #fff;
            border-bottom: 1px solid #fff;
            margin-bottom: -1px;
        }

        .tab-content {
            display: none;
        }

        .tab-content.active {
            display: block;
        }
        /* 添加重置按钮样式 */
        .reset-button {
            width: 100px;
            padding: 10px;
            background-color: #6c757d;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
            margin-left: 10px;
            transition: background-color 0.3s;
        }

        .reset-button:hover {
            background-color: #5a6268;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>接口请求工具</h1>
        </div>
        
        <div class="request-panel">
            <div class="url-section">
                <select class="method-select" id="methodSelect">
                    <option value="GET">GET</option>
                    <option value="POST">POST</option>
                    <option value="PUT">PUT</option>
                    <option value="DELETE">DELETE</option>
                </select>
                <input type="text" class="url-input" id="urlInput" placeholder="请输入接口地址，例如：https://jsonplaceholder.typicode.com/posts/1">
                <button class="send-button" id="sendButton">发送</button>
                <button class="reset-button" id="resetButton">重置</button>
            </div>
            
            <div class="tabs">
                <div class="tab active" data-tab="params">参数</div>
                <div class="tab" data-tab="files">文件上传</div>
            </div>
            
            <div class="params-section tab-content active" id="paramsTab">
                <h3 class="section-title">请求参数</h3>
                <div id="paramContainer">
                    <div class="param-inputs">
                        <input type="text" class="param-key" placeholder="参数名">
                        <input type="text" class="param-value" placeholder="参数值">
                        <button class="remove-param">-</button>
                    </div>
                </div>
                <button class="add-param">+ 添加参数</button>
            </div>
            
            <div class="file-upload-section tab-content" id="filesTab">
                <h3 class="section-title">文件上传</h3>
                <div id="fileContainer">
                    <div class="file-input-container">
                        <input type="text" class="file-key" placeholder="字段名（如：file）" value="file">
                        <input type="file" class="file-input">
                        <button class="file-remove">-</button>
                    </div>
                </div>
                <button class="add-file">+ 添加文件</button>
            </div>
        </div>
        
        <div class="response-panel">
            <div class="response-header">
                <h3 class="section-title">响应结果</h3>
                <div class="status-indicator hidden" id="statusIndicator"></div>
            </div>
            <div class="response-content" id="responseContent">
                点击"发送"按钮发起请求，响应内容将显示在这里
            </div>
        </div>
    </div>

    <script>
        // 获取DOM元素
        const methodSelect = document.getElementById('methodSelect');
        const urlInput = document.getElementById('urlInput');
        const sendButton = document.getElementById('sendButton');
        const paramContainer = document.getElementById('paramContainer');
        const fileContainer = document.getElementById('fileContainer');
        const responseContent = document.getElementById('responseContent');
        const statusIndicator = document.getElementById('statusIndicator');
        const tabs = document.querySelectorAll('.tab');
        const tabContents = document.querySelectorAll('.tab-content');

        // Tab切换功能
        tabs.forEach(tab => {
            tab.addEventListener('click', () => {
                // 移除所有活动状态
                tabs.forEach(t => t.classList.remove('active'));
                tabContents.forEach(content => content.classList.remove('active'));
                
                // 添加当前活动状态
                tab.classList.add('active');
                const tabId = tab.getAttribute('data-tab');
                document.getElementById(tabId + 'Tab').classList.add('active');
            });
        });

        // 添加参数行
        function addParamRow() {
            const paramRow = document.createElement('div');
            paramRow.className = 'param-inputs';
            paramRow.innerHTML = `
                <input type="text" class="param-key" placeholder="参数名">
                <input type="text" class="param-value" placeholder="参数值">
                <button class="remove-param">-</button>
            `;
            paramContainer.appendChild(paramRow);
            
            // 为新添加的删除按钮绑定事件
            paramRow.querySelector('.remove-param').addEventListener('click', function() {
                if (paramContainer.children.length > 1) {
                    paramContainer.removeChild(paramRow);
                } else {
                    // 如果是最后一个参数行，清空内容而不是删除
                    paramRow.querySelector('.param-key').value = '';
                    paramRow.querySelector('.param-value').value = '';
                }
            });
        }

        // 添加文件上传行
        function addFileRow() {
            const fileRow = document.createElement('div');
            fileRow.className = 'file-input-container';
            fileRow.innerHTML = `
                <input type="text" class="file-key" placeholder="字段名（如：file）" value="file">
                <input type="file" class="file-input">
                <button class="file-remove">-</button>
            `;
            fileContainer.appendChild(fileRow);
            
            // 为新添加的删除按钮绑定事件
            fileRow.querySelector('.file-remove').addEventListener('click', function() {
                if (fileContainer.children.length > 1) {
                    fileContainer.removeChild(fileRow);
                }
            });
        }

        // 绑定添加参数按钮事件
        document.querySelector('.add-param').addEventListener('click', addParamRow);
        
        // 绑定添加文件按钮事件
        document.querySelector('.add-file').addEventListener('click', addFileRow);

        // 为初始的删除按钮绑定事件
        document.querySelector('.param-inputs .remove-param').addEventListener('click', function() {
            const paramRow = this.parentElement;
            if (paramContainer.children.length > 1) {
                paramContainer.removeChild(paramRow);
            } else {
                // 如果是最后一个参数行，清空内容而不是删除
                paramRow.querySelector('.param-key').value = '';
                paramRow.querySelector('.param-value').value = '';
            }
        });

        // 为初始的文件删除按钮绑定事件
        document.querySelector('.file-input-container .file-remove').addEventListener('click', function() {
            const fileRow = this.parentElement;
            if (fileContainer.children.length > 1) {
                fileContainer.removeChild(fileRow);
            }
        });

        // 收集参数
        function collectParams() {
            const params = {};
            const paramRows = document.querySelectorAll('.param-inputs');
            
            paramRows.forEach(row => {
                const keyInput = row.querySelector('.param-key');
                const valueInput = row.querySelector('.param-value');
                
                if (keyInput.value.trim() !== '' && valueInput.value.trim() !== '') {
                    params[keyInput.value] = valueInput.value;
                }
            });
            
            return params;
        }

        // 收集文件
        function collectFiles() {
            const files = [];
            const fileRows = document.querySelectorAll('.file-input-container');
            
            fileRows.forEach(row => {
                const keyInput = row.querySelector('.file-key');
                const fileInput = row.querySelector('.file-input');
                
                if (keyInput.value.trim() !== '' && fileInput.files.length > 0) {
                    files.push({
                        key: keyInput.value,
                        file: fileInput.files[0]
                    });
                }
            });
            
            return files;
        }

        // 构建查询字符串
        function buildQueryString(params) {
            return Object.keys(params)
                .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
                .join('&');
        }

        // 发送请求
        async function sendRequest() {
            const method = methodSelect.value;
            let url = urlInput.value.trim();
            const params = collectParams();
            const files = collectFiles();
            
            // 显示加载状态
            responseContent.innerHTML = '<div class="loading"><div class="spinner"></div><p>正在发送请求...</p></div>';
            statusIndicator.classList.add('hidden');
            
            try {
                // 创建FormData对象
                const formData = new FormData();
                
                // 如果有文件，使用FormData
                if (files.length > 0) {
                    // 添加文件到FormData
                    files.forEach(fileObj => {
                        formData.append(fileObj.key, fileObj.file);
                    });
                    
                    // 添加普通参数到FormData
                    Object.keys(params).forEach(key => {
                        formData.append(key, params[key]);
                    });
                    
                    // 配置请求选项
                    const options = {
                        method: method,
                        body: formData
                        // 注意：不要设置Content-Type头，让浏览器自动设置multipart/form-data
                    };
                    
                    // 发送请求
                    const response = await fetch(url, options);
                    const contentType = response.headers.get('content-type');
                    
                    // 更新状态指示器
                    statusIndicator.textContent = `${response.status} ${response.statusText}`;
                    statusIndicator.className = response.ok ? 'status-indicator status-success' : 'status-indicator status-error';
                    statusIndicator.classList.remove('hidden');
                    
                    // 处理响应
                    if (contentType && contentType.includes('application/json')) {
                        const data = await response.json();
                        responseContent.textContent = JSON.stringify(data, null, 2);
                    } else {
                        const text = await response.text();
                        responseContent.textContent = text;
                    }
                } 
                // 没有文件的情况，保持原来的逻辑
                else {
                    // 处理GET请求的参数
                    if (method === 'GET' && Object.keys(params).length > 0) {
                        const queryString = buildQueryString(params);
                        url += (url.includes('?') ? '&' : '?') + queryString;
                    }
                    
                    // 配置请求选项
                    const options = {
                        method: method,
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    };
                    
                    // 处理POST/PUT请求的参数
                    if ((method === 'POST' || method === 'PUT') && Object.keys(params).length > 0) {
                        options.body = JSON.stringify(params);
                    }
                    
                    // 发送请求
                    const response = await fetch(url, options);
                    const contentType = response.headers.get('content-type');
                    
                    // 更新状态指示器
                    statusIndicator.textContent = `${response.status} ${response.statusText}`;
                    statusIndicator.className = response.ok ? 'status-indicator status-success' : 'status-indicator status-error';
                    statusIndicator.classList.remove('hidden');
                    
                    // 处理响应
                    if (contentType && contentType.includes('application/json')) {
                        const data = await response.json();
                        responseContent.textContent = JSON.stringify(data, null, 2);
                    } else {
                        const text = await response.text();
                        responseContent.textContent = text;
                    }
                }
                
            } catch (error) {
                // 错误处理
                statusIndicator.textContent = '请求失败';
                statusIndicator.className = 'status-indicator status-error';
                statusIndicator.classList.remove('hidden');
                responseContent.textContent = `错误信息: ${error.message}`;
            }
        }

        // 绑定发送按钮事件
        sendButton.addEventListener('click', sendRequest);

        // 示例数据填充
        urlInput.value = window.location.origin+ '/';
        document.querySelector('.param-key').value = 'userId';
        document.querySelector('.param-value').value = '1';

        // 添加重置按钮功能
        const resetButton = document.getElementById('resetButton');

        function resetParams() {
            // 重置URL
            urlInput.value = '';
            
            // 重置参数部分 - 保留一行并清空内容
            const paramRows = document.querySelectorAll('.param-inputs');
            paramRows.forEach((row, index) => {
                if (index === 0) {
                    // 第一行保留并清空
                    row.querySelector('.param-key').value = '';
                    row.querySelector('.param-value').value = '';
                } else {
                    // 其他行删除
                    paramContainer.removeChild(row);
                }
            });
            
            // 重置文件部分 - 保留一行并清空内容
            const fileRows = document.querySelectorAll('.file-input-container');
            fileRows.forEach((row, index) => {
                if (index === 0) {
                    // 第一行保留并清空
                    row.querySelector('.file-key').value = 'file';
                    row.querySelector('.file-input').value = '';
                } else {
                    // 其他行删除
                    fileContainer.removeChild(row);
                }
            });
            
            // 清空响应区域
            responseContent.textContent = '点击"发送"按钮发起请求，响应内容将显示在这里';
            statusIndicator.classList.add('hidden');
        }

        // 绑定重置按钮事件
        resetButton.addEventListener('click', resetParams);
    </script>
</body>
</html>